package org.bichko.cydot;

import static org.bichko.cydot.DotParser.Symbol.*;
import org.bichko.cydot.DotParser.*;

%%

%ignorecase
%unicode
%final
%public
%class DotScanner
%function next
%type Symbol

%state MAIN ATTR XML QUOTED

%{
  // max 32 nested states
  private int[] states = new int[32];
  private int state_ptr = 0;
  private StringBuilder buf = null;
  private int xml_level = 0;
  private char quote = '\0';
  
  private String text = null;

  public String getText() {
    return text;
  }
  
  private int enter(int state){
    yybegin(state);
    return states[++state_ptr] = yystate();
  }

  private int leave(){
    yybegin(states[--state_ptr]);
    return yystate();
  }
  
  private Symbol token(Symbol symbol) {
    text = null;
    return symbol;
  }
  
  private Symbol ttext() {
    text = yytext();
    return TEXT;
  }

  private Symbol ttext(CharSequence text) {
    this.text = text.toString();
    return TEXT;
  }

  private void error(String message) {
    throw new DotFormatException(message);
  }
%}

%init{
    states[0] = yystate();
    enter(MAIN);
%init}

ID=[:jletterdigit:]|[_\.]
NEWLINE=\r|\n|\r\n
WS=[\ \t\n\r]
QUOTE=[\"']
ESCAPE=\\[\"']
%% 

<MAIN> {

  "//"[^\n\r]* { }
  "/*"(.|[\n\r])*"*/" { }
  ^#[^\n\r]* { }

  strict { return token(STRICT); }
  graph { return token(GRAPH); }
  digraph { return token(DIGRAPH); }
  subgraph { return token(SUBGRAPH); }
  node { return token(NODE); }
  edge { return token(EDGE); }
  
  "--" { return token(EDGEOP_UND); }
  "->" { return token(EDGEOP_DIR); }
  
  ";" { return token(NEWREC); }
  "{" { return token(BRACE_OPEN); }
  "}" { return token(BRACE_CLOSE); }
  "[" { enter(ATTR); return token(BRACKET_OPEN); }
  

}

<QUOTED> {
    [^\\\"']* { buf.append(yytext()); }
    {ESCAPE} { buf.append(yytext().substring(1)); }
    \\ { buf.append('\\'); }
    <<EOF>> { error("unterminated string"); }
}

<XML> {
  [^<>]+ { buf.append(yytext()); }
  "<" { xml_level++; buf.append('<'); }
  ">" { if(--xml_level == 0) { leave(); return ttext(buf); } else { buf.append('>'); } }
  <<EOF>> { error("unterminated xml constant"); }
}

<ATTR> {
  "]" { leave(); return token(BRACKET_CLOSE); }
}

{QUOTE} {
    if(yystate() != QUOTED) {
        enter(QUOTED);
        buf = new StringBuilder();
        quote = yytext().charAt(0);
    }
    else if(quote == yytext().charAt(0)) {
        leave();
        return ttext(buf);
    }
    else {
        buf.append(yytext());
    }
}

"<" { enter(XML); xml_level++; buf = new StringBuilder(); }

"," { return token(COMMA); }
"=" { return token(EQUALS); }
{ID}+ { return ttext(); }
\:{ID} {}

{WS}+ { }
<<EOF>> { if(state_ptr == 1) { return token(EOF); } else {error("unexpected EOF"); } }
. { error("unexpected token: " + yytext()); }
